﻿namespace System.NETool;

public static class Dir
{
    /// <summary>
    /// 处理文件夹名称，确保末尾包括目录分隔符
    /// </summary>
    /// <param name="this">文件夹名称</param>
    /// <returns>处理后的文件夹名称</returns>
    public static string DealPath(this string @this)
    {
        return string.IsNullOrEmpty(@this) ? @this : @this[^1] == Path.DirectorySeparatorChar ? @this : @this + Path.DirectorySeparatorChar;
    }

    /// <summary>
    /// 验证文件夹路径是否有效
    /// </summary>
    /// <param name="dir">文件夹路径</param>
    /// <returns>如果有效则返回 true</returns>
    /// <exception cref="ArgumentNullException">当文件夹路径为空时抛出</exception>
    /// <exception cref="ArgumentException">当文件夹路径不包含根路径时抛出</exception>
    public static bool IsValid(string dir)
    {
        if (string.IsNullOrEmpty(dir))
        {
            Console.WriteLine("The directory cannot be empty.");
            return false;
        }

        if (!IsPathRooted(dir))
        {
            Console.WriteLine("The directory does not contain a root path.");
            return false;
        }

        return dir.IndexOfAny(System.IO.Path.GetInvalidPathChars()) < 0;
    }

    /// <summary>
    /// 根据文件名生成有效的绝对文件路径，文件名不包含根路径时为当前文件夹，并确保文件夹存在
    /// </summary>
    /// <param name="fileName">文件名</param>
    /// <returns></returns>
    /// <exception cref="ArgumentException"></exception>
    public static string VerifyFileName(string fileName)
    {
        ArgumentException.ThrowIfNullOrEmpty(fileName);

        if (!IsPathRooted(fileName)) fileName = CurrentDirCombinePath(CurrentDir(), fileName);
        if (!IsValid(fileName)) throw new ArgumentException($"The file name is invalid: {fileName}");

        FileInfo fi = new(fileName);
        ArgumentException.ThrowIfNullOrEmpty(fi.Name);
        if (fi.Name.IndexOfAny(System.IO.Path.GetInvalidFileNameChars()) >= 0)
        {
            throw new ArgumentException($"The file name is invalid: {fileName}");
        }

        if (!CreateDir(fi.DirectoryName))
        {
            throw new ArgumentException($"The directory does not exist: {fi.DirectoryName}");
        }

        return fi.FullName;
    }

    /// <summary>
    /// 验证路径是否包含根路径
    /// </summary>
    /// <param name="dir">文件夹路径</param>
    /// <returns>如果包含根路径则返回 true</returns>
    public static bool IsPathRooted(string dir)
    {
        return Path.IsPathRooted(dir);
    }

    /// <summary>
    /// 创建一个目录
    /// </summary>
    /// <param name="dir">目录的绝对路径</param>
    /// <returns>如果目录创建成功或已存在则返回 true</returns>
    public static bool CreateDir(string dir)
    {
        if (dir == null) return false;
        if (!IsValid(dir)) return false;
        if (Directory.Exists(dir)) return true;

        try
        {
            Directory.CreateDirectory(dir);
            return Directory.Exists(dir);
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
            return false;
        }
    }

    /// <summary>
    /// 在指定目录下创建以年月日分级的子目录，确保末尾包括目录分隔符
    /// </summary>
    /// <param name="this">日期</param>
    /// <param name="dir">文件夹路径</param>
    /// <param name="createIfNotExist">如果目录不存在是否创建</param>
    /// <param name="withLink">是否包含链接</param>
    /// <returns>创建的子目录路径</returns>
    public static string YearMonthDayFolder(this DateTime @this, string dir, bool createIfNotExist = true, bool withLink = true)
    {
        if (!IsValid(dir)) return string.Empty;

        string result = Path.Combine(dir.DealPath(), @this.YearString(), @this.YearMonthString(withLink), @this.DateString(withLink)).DealPath();

        if (createIfNotExist)
        {
            CreateDir(result);
        }

        return result;
    }

    /// <summary>
    /// 在指定目录下创建以年月分级的子目录，确保末尾包括目录分隔符
    /// </summary>
    /// <param name="this">日期</param>
    /// <param name="dir">文件夹路径</param>
    /// <param name="createIfNotExist">如果目录不存在是否创建</param>
    /// <param name="withLink">是否包含链接</param>
    /// <returns>创建的子目录路径</returns>
    public static string YearMonthFolder(this DateTime @this, string dir, bool createIfNotExist = true, bool withLink = true)
    {
        if (!IsValid(dir)) return string.Empty;

        string result = Path.Combine(dir.DealPath(), @this.YearString(), @this.YearMonthString(withLink)).DealPath();

        if (createIfNotExist)
        {
            CreateDir(result);
        }

        return result;
    }

    /// <summary>
    /// 返回当前应用程序的基目录
    /// </summary>
    /// <returns>当前基目录</returns>
    public static string CurrentDir() => AppDomain.CurrentDomain.BaseDirectory.DealPath();

    /// <summary>
    /// 返回系统的临时文件夹路径，确保末尾包括目录分隔符
    /// </summary>
    /// <returns>临时文件夹路径</returns>
    public static string TempPath() => Path.GetTempPath().DealPath();

    /// <summary>
    /// 尝试删除非空文件夹
    /// </summary>
    /// <param name="dir">文件夹路径</param>
    /// <returns>如果删除成功或文件夹不存在则返回 true</returns>
    public static bool TryDelete(string dir)
    {
        if (!Directory.Exists(dir)) return true;

        try
        {
            Directory.Delete(dir, true);
            return !Directory.Exists(dir);
        }
        catch (IOException ex)
        {
            Console.WriteLine($"IO Exception: {ex.Message}");
            return false;
        }
        catch (UnauthorizedAccessException ex)
        {
            Console.WriteLine($"Unauthorized Access: {ex.Message}");
            return false;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"General Exception: {ex.Message}");
            return false;
        }
    }

    /// <summary>
    /// 返回当前磁盘信息
    /// </summary>
    /// <returns>当前磁盘信息</returns>
    public static DriveInfo CurrentDriveInfo() => new(CurrentDir());

    /// <summary>
    /// 返回组合指定路径信息
    /// </summary>
    /// <param name="paths">指定路径信息</param>
    /// <returns>组合后的全路径</returns>
    public static string CombinePath(params string[] paths) => Path.Combine(paths);

    /// <summary>
    /// 返回当前目录并组合指定路径信息
    /// </summary>
    /// <param name="paths">指定路径信息</param>
    /// <returns>组合后的全路径</returns>
    public static string CurrentDirCombinePath(params string[] paths)
    {
        var list = paths.ToList();
        list.Insert(0, CurrentDir());
        return Path.Combine(list.ToArray());
    }
}
